package com.service.admin.utils;

import org.apache.commons.lang3.StringUtils;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 钱包工具
 */
public class WalletUtil {


    /**
     *
     */
    /**
     * 离线签名eth
     *
     * @param to//转账的钱包地址
     * @param nonce//获取到的交易次数
     * @param gasPrice
     * @param gasLimit
     * @param value           //转账的值
     * @return
     */
    public static RawTransaction unsignedEthRawTransactionData(String to, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String value) {
        //把十进制的转换成ETH的Wei, 1ETH = 10^18 Wei
        BigDecimal realValue = Convert.toWei(value, Convert.Unit.ETHER);
        return RawTransaction.createEtherTransaction(nonce, gasPrice, gasLimit, to, realValue.toBigIntegerExact());
        //手续费= (gasPrice * gasLimit ) / 10^18 ether
    }

    /**
     * 代币的转账交易未签名数据
     *
     * @param contractAddress//合约地址
     * @param to//转账的钱包地址
     * @param nonce//获取到的交易次数
     * @param gasPrice
     * @param gasLimit
     * @param value                 //转账的值 Double 类型
     * @return
     */
    public static RawTransaction unsignedRawTransactionData(String contractAddress, String to, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, Double value, Double decimal) {
        //因为每个代币可以规定自己的小数位, 所以实际的转账值=数值 * 10^小数位
        BigDecimal realValue = unit_transfer(value,decimal);
        //0xa9059cbb代表某个代币的转账方法hex(transfer) + 对方的转账地址hex + 转账的值的hex
        String data = "0xa9059cbb" + Numeric.toHexStringNoPrefixZeroPadded(Numeric.toBigInt(to), 64) + Numeric.toHexStringNoPrefixZeroPadded(realValue.toBigInteger(), 64);
        return RawTransaction.createTransaction(nonce, gasPrice, gasLimit, contractAddress, data);
    }

    /**
     * 另一种未签名创建方法，利用Function创建 transfer
     * @param contractAddress
     * @param to
     * @param nonce
     * @param gasPrice
     * @param gasLimit
     * @param amount
     * @return
     */
    public static RawTransaction unsignedRawTransactionData(String contractAddress, String to, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, BigDecimal amount){
        //封装转账交易
        BigInteger amountMWei = Convert.toWei(amount, Convert.Unit.MWEI).toBigInteger();
        //创建RawTransaction交易对象
        Function function = new Function(
                "transfer",
                Arrays.asList(new Address(to), new Uint256(amountMWei)),
                Arrays.asList(new TypeReference<Type>() {
                }));
        String encodedFunc = FunctionEncoder.encode(function);
        //未签名交易
        return RawTransaction.createTransaction(nonce, gasPrice, gasLimit, contractAddress, encodedFunc);
    }


    /**
     * usdt的单位换算，usdt最小单位小数点后6位
     * @param amount
     * @return
     */
    public static BigDecimal usdt_unit(Double amount){
        return unit_transfer(amount,6.0);
    }


    /**
     * usdt 的单位换算，最小为小数点后6位
     * @param amount
     * @return
     */
    public static BigDecimal usdt_unit(BigInteger amount){
        //封装转账交易
        return Convert.fromWei(String.valueOf(amount), Convert.Unit.MWEI);
    }

    /**
     * 单位换算公共函数
     * @param value
     * @param decimal
     * @return
     */
    public static BigDecimal unit_transfer(Double value,Double decimal){
        return BigDecimal.valueOf(value * Math.pow(10.0, decimal));
    }


    /**
     * 获取eth余额
     * @param from
     * @return
     */
    public static BigDecimal getBalance(String from,Web3j web3j) throws IOException {
        BigInteger ethBalance = web3j.ethGetBalance(from, DefaultBlockParameterName.PENDING).send().getBalance();
        return Convert.fromWei(String.valueOf(ethBalance),Convert.Unit.ETHER);
    }


    /**
     * 查询erc20_USDT 的余额
     * @param account
     * @return
     */
    public static BigInteger getBalanceOfUsdt(String account,Web3j web3j){
        return getBalanceOfCoin(account,"",web3j);
    }


    /**
     * 获取账户代币余额
     * @param account 账户地址
     * @param coinAddress 代币地址
     * @return 代币余额 （单位：代币最小单位）
     * @throws IOException
     */
    public static BigInteger getBalanceOfCoin(String account, String coinAddress, Web3j web3j){
        Function balanceOf = new Function("balanceOf",
                Arrays.<Type>asList(new Address(account)),
                Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {
                }));

        if (coinAddress == null) {
            return null;
        }
        try{
            String value = web3j.ethCall(Transaction.createEthCallTransaction(account, coinAddress, FunctionEncoder.encode(balanceOf)), DefaultBlockParameterName.PENDING).send().getValue();
            return new BigInteger(value.substring(2), 16);
        }catch (IOException e){
            return null;
        }
    }


    /**
     * eth合约的函数调用，返回encode后的参数string
     * @param method
     * @return
     */
    private static String _encodeFunc(String method){
        if(StringUtils.isEmpty(method)){
            return null;
        }
        List<Type> inputParameters = new ArrayList<>();
        List<TypeReference<?>> outputParameters = new ArrayList<>();
        //Address tAddress = new Address(toAddress);
        //Uint256 tokenValue = new Uint256(BigDecimal.valueOf(amount).multiply(BigDecimal.TEN.pow(decimals)).toBigInteger());
        //inputParameters.add(tAddress);
        //inputParameters.add(tokenValue);
        TypeReference<Bool> typeReference = new TypeReference<Bool>() {};
        outputParameters.add(typeReference);
        Function function = new Function(method, inputParameters, outputParameters);
        return FunctionEncoder.encode(function);
    }


    /**
     * 发送账户内所有某代币
     * @param from 代币拥有地址
     * @param to 代币目标地址
     * @param coinAddress 代币合约地址
     * @param gasLimit gas值
     * @param gasPrice gas price
     * @param credentials 秘钥对象
     * @return 交易hash
     * @throws IOException
     */
//    public String sendAllCoin(String from, String to, String coinAddress, BigInteger gasLimit, BigInteger gasPrice, Credentials credentials) throws IOException {
//        BigInteger nonce = web3j.ethGetTransactionCount(from, DefaultBlockParameterName.PENDING).send().getTransactionCount();
//
//        BigInteger value = getBalanceOfCoin(from, coinAddress);
//        System.out.println(value);
//
//        Function transfer = new Function(
//                "transfer",
//                Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(to),
//                        new org.web3j.abi.datatypes.generated.Uint256(value)),
//                Collections.<TypeReference<?>>emptyList());
//        String data = FunctionEncoder.encode(transfer);
//
//        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, coinAddress, data);
//
//        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
//
//        return web3j.ethSendRawTransaction(Numeric.toHexString(signMessage)).send().getTransactionHash();
//
//    }
}
